package org.person.gift.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.person.gift.clients.GiftClient;
import org.person.gift.clients.PayClient;
import org.person.gift.constants.OrderConstant;
import org.person.gift.domain.GoodsSku;
import org.person.gift.domain.OrderGift;
import org.person.gift.domain.OrderGiftAddress;
import org.person.gift.domain.OrderGiftItem;
import org.person.gift.dto.PlaceOrderDTO;
import org.person.gift.exception.BusinessException;
import org.person.gift.exception.ResponseCode;
import org.person.gift.exception.SystemAssert;
import org.person.gift.mapper.OrderGiftMapper;
import org.person.gift.service.IOrderGiftAddressService;
import org.person.gift.service.IOrderGiftItemService;
import org.person.gift.service.IOrderGiftService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.person.gift.strategy.SplitOrderStrategy;
import org.person.gift.strategy.SplitOrderStrategyFactory;
import org.person.gift.util.AjaxResult;
import org.person.gift.util.CodeGenerateUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author stark
 * @since 2024-02-22
 */
@Service
public class OrderGiftServiceImpl extends ServiceImpl<OrderGiftMapper, OrderGift> implements IOrderGiftService {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private GiftClient giftClient;

    @Autowired
    private IOrderGiftItemService orderGiftItemService;

    @Autowired
    private IOrderGiftAddressService orderGiftAddressService;

    @Autowired
    private PayClient payClient;

    @Value("${person.splitOrder.strategy}")
    private String splitOrderStrategy;

    @Override
    public void placeOrder(PlaceOrderDTO dto) {
        // 1 参数校验
        SystemAssert.isNotBlank(dto.getParentOrderNo().toString(), ResponseCode.RESPONSE_CODE_500);
        // 现在这创建好一个支付单的单号,将这个支付单的单号传递到支付服务和前端
        String unionPayOrderSn = CodeGenerateUtils.generateUnionPaySn();
        dto.setUnionPayOrderSn(unionPayOrderSn);

        // 发送事务消息
        Message<PlaceOrderDTO> message = MessageBuilder.withPayload(dto).build();
        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
                OrderConstant.PLACE_ORDER_TX_PRODUCER_GROUP,
                OrderConstant.PLACE_ORDER_TX_TOPIC + ":" + OrderConstant.PLACE_ORDER_TX_TAG,
                message, null
        );

        if(sendResult.getSendStatus() != SendStatus.SEND_OK){
            throw new BusinessException("下单失败!");
        }
    }

    @Override
    @Transactional
    public void placeOrderExecuteLocalTransaction(PlaceOrderDTO dto) {
        // 1 根据父订单编号获取分布式锁锁对象
        RLock lock = redissonClient.getLock(dto.getParentOrderNo().toString());
        try {
            // 上锁
            lock.lock();
            // 2 根据parentOrderNo查询
            Wrapper<OrderGift> wrapper = new EntityWrapper<OrderGift>()
                    .eq("parent_order_no", dto.getParentOrderNo());
            int count = selectCount(wrapper);
            if(count > 0){
                throw new BusinessException("请不要重复下单!!");
            }
            // 3 如果不存在,则进行下单操作
            submitOrder(dto);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 释放锁
            lock.unlock();
        }

    }
    private void submitOrder(PlaceOrderDTO dto) {
        // 1 获取当前下单时所有商品的信息 - 调用礼物服务
        Map<Long, Integer> skuInfos = dto.getSkuInfos();
        Set<Long> skuIds = skuInfos.keySet();
        AjaxResult ajaxResult = giftClient.getSkusByIds(skuIds);
        if(!ajaxResult.isSuccess()){
            throw new RuntimeException(ajaxResult.getMessage());
        }
        String jsonString = JSONObject.toJSONString(ajaxResult.getResultObj());
        List<GoodsSku> goodsSkus = JSONArray.parseArray(jsonString, GoodsSku.class);

        // 2 拆单,要按照店铺进行拆单  Map<店铺信息,List<GoodsSku>>
        // 策略模式 + 工厂模式
        SplitOrderStrategy strategy = SplitOrderStrategyFactory.getStrategy(splitOrderStrategy);
        Map<Long, List<GoodsSku>> listMap = strategy.splitOrder(goodsSkus);

        // 3 构建OrderGift,并保存
        Set<Map.Entry<Long, List<GoodsSku>>> entries = listMap.entrySet();
        for (Map.Entry<Long, List<GoodsSku>> entry : entries) {
            List<GoodsSku> skus = entry.getValue();
            saveOrder(dto, skuInfos, skus);
        }

        Message<String> message = MessageBuilder.withPayload(dto.getParentOrderNo().toString()).build();
        SendResult sendResult = rocketMQTemplate.syncSend(
                OrderConstant.PLACE_ORDER_TIMEOUT_CANCEL_TOPIC + ":" + OrderConstant.PLACE_ORDER_TIMEOUT_CANCEL_TAG,
                message, 3000, 18
        );
        if(sendResult.getSendStatus() != SendStatus.SEND_OK){
            throw new BusinessException("发送延迟消息失败!!");
        }
    }
    private void saveOrder(PlaceOrderDTO dto, Map<Long, Integer> skuInfos, List<GoodsSku> skus) {
        // 每次循环,都表示是一个店铺的商品,构建OrderGift对象
        OrderGift orderGift = new OrderGift();
        // 指定了订单ID,订单ID建议不要自增长,分库分表
        orderGift.setId(IdUtil.getSnowflakeNextId());
        orderGift.setCreateTime(new Date());
        orderGift.setParentOrderNo(dto.getParentOrderNo().toString());
        // 订单编号,是我们创建好保存到数据库的,一般来说订单编号的格式: yyyyMMddHHmmss+随机字符串
        orderGift.setOrderNo(CodeGenerateUtils.generateOrderSn(dto.getUserId()));
        orderGift.setStatus(0);
        orderGift.setUserId(dto.getUserId());
        orderGift.setUsername(dto.getUsername());
        orderGift.setRemark(dto.getRemark());
        orderGift.setPayType(dto.getPayType());
        orderGift.setLastPayTime(DateUtils.addMinutes(new Date(),30));
        // 最后确认时间,收到货以后,才能够确定最后确认时间
        // 支付总价: 当前店铺下所有商品的总价格  totalAmount
        // 数量: 当前购买商品的数量相加
        BigDecimal totalAmount = BigDecimal.ZERO;
        Integer totalCount = 0;
        List<OrderGiftItem> items = new ArrayList<>();
        for (GoodsSku goodsSku : skus) {
            Integer num = skuInfos.get(goodsSku.getId());
            // 计算价格相加
            BigDecimal multiply = goodsSku.getPrice().multiply(new BigDecimal(num));
            totalAmount = totalAmount.add(multiply);
            // 计算数量
            totalCount = totalCount + num;
            // 构建每个订单的明细,保存数据库
            // 4 构建OrderGiftItem 并保存
            OrderGiftItem item = new OrderGiftItem();
            item.setOrderId(orderGift.getId());
            item.setOrderNo(orderGift.getOrderNo());
            item.setAmount(goodsSku.getPrice());
            item.setCount(num);
            item.setCreateTime(new Date());
            item.setSkuId(goodsSku.getId());
            item.setSkuInfo(goodsSku.getSkuName());
            item.setSpuId(goodsSku.getSpuId());
            item.setSpuName(goodsSku.getSpuName());
            items.add(item);
        }
        orderGift.setTotalAmount(totalAmount);
        orderGift.setTotalCount(totalCount);
        // 保存订单信息
        insert(orderGift);
        // 保存订单明细信息
        orderGiftItemService.insertBatch(items);
        // 5 保存OrderGiftAddress
        OrderGiftAddress address = dto.getAddress();
        address.setOrderId(orderGift.getId());
        address.setOrderSn(orderGift.getOrderNo());
        address.setUserId(dto.getUserId());
        orderGiftAddressService.insert(address);
    }

    @Override
    public Boolean placeOrderCheckLocalTransaction(PlaceOrderDTO dto) {
        // 根据父订单编号,查询订单是否已存在
        Wrapper<OrderGift> wrapper = new EntityWrapper<OrderGift>()
                .eq("parent_order_no", dto.getParentOrderNo());
        int count = selectCount(wrapper);

        // 如果已存在,则说明本地事务执行成功,否则就是失败
        if(count > 0){
            return true;
        }

        return false;
    }

    @Override
    public void cancelOrder(String parentOrderNo) {
        // 1 根据parentOrderNo查询未支付的订单信息
        List<OrderGift> orderGifts = selectList(new EntityWrapper<OrderGift>()
                .eq("parent_order_no", parentOrderNo)
                .and()
                .eq("status",0));

        // 2 如果未支付订单为空,说明都已经支付过了,如果未支付订单不为空,就说明还没有支付
        if(CollectionUtil.isNotEmpty(orderGifts)){
            // 2.1 修改订单状态 = 超时取消
            for (OrderGift orderGift : orderGifts) {
                orderGift.setStatus(-1);
                orderGift.setUpdateTime(new Date());
            }
            updateBatchById(orderGifts);

            // 2.2 调用礼物服务,释放库存
            List<Long> orderIds = orderGifts.stream().map(OrderGift::getId).collect(Collectors.toList());
            List<OrderGiftItem> items = orderGiftItemService.selectList(
                    new EntityWrapper<OrderGiftItem>().in("order_id", orderIds));

            Map<Long, Integer> map = items.stream().collect(
                    Collectors.toMap(OrderGiftItem::getSkuId, OrderGiftItem::getCount));
            giftClient.releaseLockStock(map);

            // 2.3 调用支付服务,取消支付单
            payClient.cancelPayOrder(parentOrderNo);
        }
    }
}
